package com.zt.chatgpt.domain.security.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.binary.Base64;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author: zt
 * @Description: 获取，解析，验证JwtToken 的工具类
 * @create: 2024-05-08 23:49
 */

public class JwtUtil {

    /**
     * 默认密钥
     */
    private static final String DEFAULT_BASE64_ENCODED_SECRET_KEY = "B*B^";
    /**
     * 默认签名算法
     */
    private static final SignatureAlgorithm DEFAULT_SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;


    public JwtUtil() {
        this(DEFAULT_BASE64_ENCODED_SECRET_KEY, DEFAULT_SIGNATURE_ALGORITHM);
    }

    private final String base64EncodedSecretKey;
    private final SignatureAlgorithm signatureAlgorithm;

    public JwtUtil(String secretKey, SignatureAlgorithm signatureAlgorithm) {
        // 把密钥进行base64编码，以便在不暴露密钥的情况下使用
        this.base64EncodedSecretKey = Base64.encodeBase64String(secretKey.getBytes());
        this.signatureAlgorithm = signatureAlgorithm;
    }


    /**
     * 构造jwt
     * jwt字符串包括三个部分
     * 1. header
     * -当前字符串的类型，一般都是“JWT”
     * -哪种算法加密，“HS256”或者其他的加密算法
     * 所以一般都是固定的，没有什么变化
     * 2. payload
     * 一般有四个最常见的标准字段（下面有）
     * iat：签发时间，也就是这个jwt什么时候生成的
     * jti：JWT的唯一标识
     * issuer：签发人，一般都是username或者userId
     * exp：过期时间
     * 3. signature
     */
    public String encode(String issuer, long expireTime, Map<String, Object> claims) {
        // claims 存放在jwt中的一些非隐私信息,即载荷部分
        if (claims == null) {
            claims = new HashMap<>();
        }
        long nowMillis = System.currentTimeMillis();
        // 构建jwtToken
        JwtBuilder builder = Jwts.builder()
                // payload
                .setClaims(claims)
                // jwt的唯一标识
                .setId(UUID.randomUUID().toString())
                // 签发时间
                .setIssuedAt(new Date(nowMillis))
                // 签发人
                .setSubject(issuer)
                // header
                .signWith(signatureAlgorithm, base64EncodedSecretKey);
        if (expireTime >= 0) {
            // 过期时间
            builder.setExpiration(new Date(nowMillis + expireTime));
        }
        return builder.compact();
    }

    /**
     * 解析jwt
     * Claims本质是一个Map，里面存放了jwt的payload部分
     */
    public Claims decode(String jwt) {
        return Jwts.parser()
                // 设置签名密钥
                .setSigningKey(base64EncodedSecretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }

    /**
     * 验证jwtTokens是否合法
     */
    public boolean isVerify(String jwtToken) {
        // 这个是官方的校验规则，这里只写了一个”校验算法“，可以自己加
        Algorithm algorithm = null;
        switch (signatureAlgorithm) {
            case HS256:
                algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodedSecretKey));
                break;
            default:
                throw new RuntimeException("不支持该算法");
        }
        JWTVerifier verifier = JWT.require(algorithm).build();
        verifier.verify(jwtToken);
        // 校验不通过会抛出异常
        // 判断合法的标准：1. 头部和荷载部分没有篡改过。2. 没有过期
        return true;
    }


}
